package com.yitao.cms.service.expressage;

import com.querydsl.core.types.Order;
import com.querydsl.core.types.OrderSpecifier;
import com.skipper.base.bean.CommonStatus;
import com.skipper.base.bean.DataOutput;
import com.skipper.base.bean.PagedInput;
import com.skipper.base.bean.PagedOutput;
import com.skipper.base.data.querydsl.PredicateBuilder;
import com.skipper.base.service.GenericEntityService;
import com.yitao.client.constant.OrderStatusEmnu;
import com.yitao.client.dto.expressage.ExpressageMatchingDetailDto;
import com.yitao.client.model.expressage.ExpressageMatchingDetailModel;
import com.yitao.client.model.expressage.QExpressageMatchingDetailModel;
import com.yitao.client.model.expressage.ServicesCompanyInfoModel;
import com.yitao.cms.constant.UserSession;
import com.yitao.cms.model.operationUser.OperationUserCmsModel;
import com.yitao.cms.service.order.CmsServicesCompanyInfoService;
import com.yitao.cms.utils.BaseUtils;
import com.yitao.cms.utils.ExcelExportUtil;
import com.yitao.cms.utils.UpdateFileUtils;
import com.yitao.cms.vo.expressage.ExpressageMatchingDetailVo;
import org.apache.commons.lang.StringUtils;
import org.apache.http.entity.ContentType;
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.usermodel.Workbook;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.mock.web.MockMultipartFile;
import org.springframework.stereotype.Service;
import org.springframework.transaction.interceptor.TransactionAspectSupport;
import org.springframework.util.CollectionUtils;
import org.springframework.web.multipart.MultipartFile;

import javax.persistence.EntityManager;
import javax.servlet.http.HttpServletRequest;
import javax.transaction.Transactional;
import java.io.*;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;

/**
 * @Date: 2019/6/12 11:51
 * @Author: Kien
 */
@Service
public class ExpressageMatchingDetailCmsService extends GenericEntityService<ExpressageMatchingDetailModel, ExpressageMatchingDetailDto, QExpressageMatchingDetailModel> {
    @Autowired
    private CmsServicesCompanyInfoService cmsServicesCompanyInfoService;

    @Value("${excel_upload_location}")
    private String EXCEL_UPLOAD_LOCATION;

    private BaseUtils baseUtils = BaseUtils.getInstance();

    private static final String ERRORCODE = "0";
    private static final String SUCCESS = "1";
    private static final String COMCODEISNULL = "物流公司编码为空";
    private static final String COMERROR = "物流公司编码错误";
    private static final String EXPRESSAGESTATUSERROR = "未找到对应物流状态";
    private static final String MATCHINGKEYWORDISNULL = "物流信息匹配字为空";
    private static final String LOGISTICSSTATUSMSGISNULL = "物流显示状态为空";

    public ExpressageMatchingDetailCmsService(EntityManager entityManager) {
        super(entityManager);
    }

    /**
     *
     * 功能描述: 物流信息匹配字导入----前端不支持同时传输文件流和其他参数，改成使用下面那个方法
     *
     * @param file excel表格
     * @param companyName
     * @param nameAbbr
     * @param logoUrl
     * @param idx
     * @param request
     * @return
     * @auther: Kien
     * @date: 2019/6/12 11:55
     */
//    @Transactional
//    public DataOutput importExcel(MultipartFile file, String companyName, String nameAbbr, String logoUrl, Integer idx, HttpServletRequest request){
//        // 获取登陆用户信息
//        OperationUserCmsModel userSession = UserSession.getUserSession(request);
//        if(userSession == null) {
//            return new DataOutput(CommonStatus.UNAUTHORIZED);
//        }
//        if(baseUtils.parametersIsBlank(companyName, nameAbbr, logoUrl)) {
//            return new DataOutput(CommonStatus.BAD_REQUEST.getCode(), "物流公司主信息不能为空");
//        }
//        ServicesCompanyInfoModel servicesCompanyInfoModel = cmsServicesCompanyInfoService.save(companyName, nameAbbr, logoUrl, idx, userSession.getId());
//        Map<String, Object> map = analysisExcel(file, servicesCompanyInfoModel);
//        if(map.get("status").toString().equals(ERRORCODE)) {
//            // 需要让事务回滚
//            TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
//            return new DataOutput(CommonStatus.BAD_REQUEST.getCode(), map.get("message").toString());
//        }
//        List list = (List) map.get("list");
//        repository.save(list);// 新增
//        return new DataOutput();
//    }

    /**
     *
     * 功能描述: 物流信息匹配字导入
     *
     * @param excelPath 文件路径
     * @param companyName 公司名称
     * @param nameAbbr 物流公司编码
     * @param
     *
     * @return
     * @auther: Kien
     * @date: 2019/7/2 9:55
     */
    @Transactional
    public DataOutput importExcel(String excelPath, String companyName, String nameAbbr, String logoUrl, Integer idx, HttpServletRequest request){
        String filePath = excelPath.substring(0, excelPath.lastIndexOf("/"));
        logger.info("文件路径为: " + filePath);
        OperationUserCmsModel userSession = UserSession.getUserSession(request);
        if(userSession == null) {
            UpdateFileUtils.delAllFile(filePath);
            return new DataOutput(CommonStatus.UNAUTHORIZED);
        }
        if(baseUtils.parametersIsBlank(companyName, nameAbbr, logoUrl)) {
            UpdateFileUtils.delAllFile(filePath);
            return new DataOutput(CommonStatus.BAD_REQUEST.getCode(), "物流公司主信息不能为空");
        }
        ServicesCompanyInfoModel servicesCompanyInfoModel = cmsServicesCompanyInfoService.save(companyName, nameAbbr, logoUrl, idx, userSession.getId());
        MultipartFile multipartFile = fileConversionMultipartFile(excelPath);
        Map<String, Object> map = analysisExcel(multipartFile, servicesCompanyInfoModel);
        if(map.get("status").toString().equals(ERRORCODE)) {
            // 需要让事务回滚
            UpdateFileUtils.delAllFile(filePath);
            TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
            return new DataOutput(CommonStatus.BAD_REQUEST.getCode(), map.get("message").toString());
        }
        List list = (List) map.get("list");
        repository.save(list);// 新增
        // 新增完成后，删除文件，将文件路径按最后一个/拆分
        UpdateFileUtils.delAllFile(filePath);
        return new DataOutput();
    }

    /**
     *
     * 功能描述: 将文件转换成MultipartFile
     *
     * @param
     * @return
     * @auther: Kien
     * @date: 2019/7/1 18:45
     */
    public MultipartFile fileConversionMultipartFile(String fileUrl){
        File file = new File(fileUrl);
        FileInputStream fileInputStream = null;
        try {
            fileInputStream = new FileInputStream(file);
            MultipartFile multipartFile = new MockMultipartFile(file.getName(), file.getName(),
                    ContentType.APPLICATION_OCTET_STREAM.toString(), fileInputStream);
            return multipartFile;
        } catch (Exception e) {
            return null;
        }
    }

    /**
     *
     * 功能描述: 解析excel
     *
     * @param file excel文件
     * @param servicesCompanyInfoModel 物流主信息
     * @return
     * @auther: Kien
     * @date: 2019/6/12 14:17
     */
    public Map<String, Object> analysisExcel(MultipartFile file, ServicesCompanyInfoModel servicesCompanyInfoModel) {
        Date date = new Date();
        Workbook wb = null;
        Map<String, Object> map = new HashMap<>(); // 返回信息
        List<ExpressageMatchingDetailModel> list = new ArrayList<>();
        List<String> tempList = new ArrayList<>();
        //获取文件文件内容
        try {
            if (ExcelExportUtil.isExcel2007(file.getOriginalFilename())) {
                wb = new XSSFWorkbook(file.getInputStream());
            } else {
                wb = new HSSFWorkbook(file.getInputStream());
            }
        } catch (IOException e) {
            e.printStackTrace();
            return null;
        }
        //读取文件信息
        Sheet sheet = wb.getSheetAt(0);//获取第一张表
        logger.info("表格总共: " + sheet.getLastRowNum() + " 行");
        // 物流公司编码，用于对比是否属于同一个快递公司，第一条信息不要对比，后面每条都需对比，如果不一样，不要重新去物流公司查询
        String tempComCode = null;
        for (int i = 1; i <= sheet.getLastRowNum(); i++) {
            Row row = sheet.getRow(i);//获取第i行数据
            ExpressageMatchingDetailModel expressageMatchingDetailModel = new ExpressageMatchingDetailModel();
            String comCode = ExcelExportUtil.cellType(row.getCell(0)).trim();//获取数据数据，首行为物流公司编码，如果没有查询整个都不进行保存，返回错误信息
            // 如果为空
            if (comCode == null) { // 编码为空
                map.put("status", ERRORCODE);
                map.put("message", "第" + i + "行" + COMCODEISNULL);
                return map;
            }
            if (!comCode.equals(servicesCompanyInfoModel.getNameAbbr())) {
                map.put("status", ERRORCODE);
                map.put("message", "第" + i + "行" + COMERROR);
                return map;
            }
            expressageMatchingDetailModel.setServicesCompanyId(servicesCompanyInfoModel.getId()); // 物流公司主信息id
            // 获取匹配关键字
            String matchingKeyword = ExcelExportUtil.cellType(row.getCell(1)).trim();
            if (StringUtils.isBlank(matchingKeyword)) {
                map.put("status", ERRORCODE);
                map.put("message", "第" + i + "行" + MATCHINGKEYWORDISNULL);
                return map;
            }
            // 判断这个关键字是否已经存在，重复就不保存
            if(isHave(tempList, matchingKeyword)) {
                continue;
            }
            // 判断这个关键字在数据库中这个物流已存在，存在就不保存
            List<String> expressageMatchingDetais = findExpressageMatchingDetaiByCompanyId(servicesCompanyInfoModel.getId());
            if(!CollectionUtils.isEmpty(expressageMatchingDetais)) {
                if(isHave(expressageMatchingDetais, matchingKeyword)) {
                    continue;
                }
            }
            expressageMatchingDetailModel.setMatchingKeyword(matchingKeyword); // 匹配关键字
            // 获取显示状态
            String logisticsStatusMsg = ExcelExportUtil.cellType(row.getCell(2)).trim();
            if (StringUtils.isBlank(logisticsStatusMsg)) {
                map.put("status", ERRORCODE);
                map.put("message", "第" + i + "行" + LOGISTICSSTATUSMSGISNULL);
                return map;
            }
            expressageMatchingDetailModel.setLogisticsStatusMsg(logisticsStatusMsg); // 物流状态
            // 获取状态的对应状态码
            Integer codeByMsg = OrderStatusEmnu.NEW_EXPRESSAGE_STATUS.getCodeByMsg(logisticsStatusMsg);
            if (codeByMsg == null) { // 没有找到对应状态码
                map.put("status", ERRORCODE);
                map.put("message", "第" + i + "行" + EXPRESSAGESTATUSERROR);
                return map;
            }
            expressageMatchingDetailModel.setLogisticsStatus(codeByMsg); // 物流状态码
            expressageMatchingDetailModel.setIsDelete(false); // 是否删除
            expressageMatchingDetailModel.setCreateTime(date); // 创建时间
            expressageMatchingDetailModel.setCreateBy(servicesCompanyInfoModel.getCreateBy()); // 创建人
            list.add(expressageMatchingDetailModel);
            tempList.add(matchingKeyword); // 将关键字保存到临时list中，用于跟下个关键字匹配
        }
        map.put("status", SUCCESS);
        map.put("list", list);
        return map;
    }

    /**
     *
     * 功能描述: 根据快递公司id删除对应所有匹配字
     *
     * @param companyId 快递公司id
     * @param userId 操作人
     * @param date 操作时间
     * @return
     * @auther: Kien
     * @date: 2019/6/13 10:13
     */
    @Transactional
    public int delExpressageMatchingDetailByCompanyId(Long companyId, Long userId, Date date){
        return repository.updateByWhere(PredicateBuilder.conjunction().and(() -> Q.servicesCompanyId.eq(companyId)).get(),
                it -> {
                    it.set(Q.isDelete, true);
                    it.set(Q.createBy, userId);
                    it.set(Q.createTime, date);
                });
    }

    /**
     *
     * 功能描述: 新增物流商关键字
     *
     * @param companyId 物流公司id
     * @param matchingKeyword 匹配关键字
     * @param logisticsStatus 物流状态码
     * @return
     * @auther: Kien
     * @date: 2019/6/13 10:49
     */
    @Transactional
    public DataOutput addMatchingKeyword(Long companyId, String matchingKeyword,Integer logisticsStatus, HttpServletRequest request) {
        Long userId = UserSession.getUserIdBySession(request);
        if(null == userId) { //
            return new DataOutput(CommonStatus.UNAUTHORIZED);
        }
        if(baseUtils.isBlank(matchingKeyword)) { // 物流状态和匹配关键字为空
            logger.info("关键字或状态为空");
            return new DataOutput(CommonStatus.BAD_REQUEST.getCode(), "关键字或状态为空");
        }
        if(null == companyId) {
            logger.info("新增关键字必须指定相应物流公司");
            return new DataOutput(CommonStatus.BAD_REQUEST.getCode(), "新增关键字必须指定相应物流公司");
        }
        if(null == logisticsStatus) {
            logger.info("未找到对应物流信息节点");
            return new DataOutput(CommonStatus.BAD_REQUEST.getCode(), "未找到对应物流信息节点");
        }
        String logisticsStatusMsg = OrderStatusEmnu.NEW_EXPRESSAGE_STATUS.getMsgByCode(logisticsStatus);
        if(logisticsStatusMsg == null) {
            logger.info("未找到对应物流信息节点");
            return new DataOutput(CommonStatus.BAD_REQUEST.getCode(), "未找到对应物流信息节点");
        }
        // 判断当前物流商是否存在有这个关键字
        List<ExpressageMatchingDetailModel> all = repository.findAll(PredicateBuilder.conjunction()
                .and(() -> Q.servicesCompanyId.eq(companyId))
                .and(() -> Q.matchingKeyword.like("%" + matchingKeyword + "%"))
                .and(() -> Q.isDelete.eq(false)).get());
        if(!CollectionUtils.isEmpty(all)) {
            logger.info("匹配字已存在");
            return new DataOutput(CommonStatus.BAD_REQUEST.getCode(), "匹配字已存在");
        }
        ExpressageMatchingDetailModel expressageMatchingDetailModel = new ExpressageMatchingDetailModel();
        expressageMatchingDetailModel.setCreateBy(userId);
        expressageMatchingDetailModel.setCreateTime(new Date());
        expressageMatchingDetailModel.setIsDelete(false);
        expressageMatchingDetailModel.setLogisticsStatus(logisticsStatus); // 物流状态码
        expressageMatchingDetailModel.setLogisticsStatusMsg(logisticsStatusMsg); // 物流状态
        expressageMatchingDetailModel.setMatchingKeyword(matchingKeyword); // 匹配字
        expressageMatchingDetailModel.setServicesCompanyId(companyId); // 物流公司
        ExpressageMatchingDetailModel save = repository.save(expressageMatchingDetailModel);
        return new DataOutput(save);
    }

    /**
     *
     * 功能描述: 获取快递公司匹配关键字
     *
     * @param companyId 物流公司id
     * @param pageNo 当前页面
     * @param pageSize 每页显示条数
     * @return
     * @auther: Kien
     * @date: 2019/6/13 20:05
     */
    public PagedOutput<ExpressageMatchingDetailVo> getExpressageMatchingDetail(Long companyId, Integer pageNo, Integer pageSize) {
        PagedInput pagedInput = new PagedInput(pageSize, pageNo);
        List<ExpressageMatchingDetailVo> list = new ArrayList<>();
        try {
            if(null == companyId) {
                return new PagedOutput("快递公司id不能为空", pagedInput, 0, list);
            }
            PagedOutput<ExpressageMatchingDetailModel> pagedOutput = repository.pagingList(PredicateBuilder.conjunction()
                    .and(() -> Q.servicesCompanyId.eq(companyId))
                    .and(() -> Q.isDelete.eq(false))
                    .get(), pagedInput, new OrderSpecifier<>(Order.ASC, Q.createTime));
            if(pagedOutput != null && pagedOutput.getData() != null) {
                // 根据快递公司id获取快递公司主信息
                ServicesCompanyInfoModel servicesCompanyInfoModel = cmsServicesCompanyInfoService.getServicesCompanyInfoModelById(companyId);
                if(null == servicesCompanyInfoModel) {
                    return new PagedOutput(pagedInput, 0, list);
                }
                list = pagedOutput.getData().stream().map(it ->{
                    ExpressageMatchingDetailVo expressageMatchingDetailVo = new ExpressageMatchingDetailVo();
                    baseUtils.convertEntityToDto(it, expressageMatchingDetailVo);
                    expressageMatchingDetailVo.setServicesCompany(servicesCompanyInfoModel.getCompanyName()); // 物流公司名字
                    return expressageMatchingDetailVo;
                }).collect(Collectors.toList());
            }
        } catch (Exception e) {
            logger.info("获取快递公司匹配字异常");
        }
        return new PagedOutput(pagedInput, 0, list);
    }

    /**
     *
     * 功能描述: 删除快递公司匹配字
     *
     * @param id 物流匹配字id
     * @param request
     * @return
     * @auther: Kien
     * @date: 2019/6/14 11:06
     */
    @Transactional
    public DataOutput delExpressageMatchingDetail(Long id, HttpServletRequest request) {
        Long userId = UserSession.getUserIdBySession(request);
        if(null == userId) {
            return new DataOutput(CommonStatus.UNAUTHORIZED);
        }
        if(null == id) {
            return new DataOutput(CommonStatus.BAD_REQUEST.getCode(), "请指定需要删除的关键字");
        }
        int i = repository.updateByWhere(PredicateBuilder.conjunction().and(() -> Q.id.eq(id)).get(),
                it -> {
                    it.set(Q.isDelete, true);
                    it.set(Q.createTime, new Date());
                    it.set(Q.createBy, userId);
                });
        if (i != 0) {
            return new DataOutput();
        }
        return new DataOutput(CommonStatus.NOT_FOUND);
    }

    /**
     *
     * 功能描述: 编辑指定物流公司匹配字
     *
     * @param id 匹配字id
     * @param matchingKeyword 匹配字
     * @param logisticsStatus 物流节点状态码
     * @param request
     * @return
     * @auther: Kien
     * @date: 2019/6/17 20:05
     */
    @Transactional
    public DataOutput updateMatchingKeyword(Long id, String matchingKeyword, Integer logisticsStatus, HttpServletRequest request) {
        Long userId = UserSession.getUserIdBySession(request);
        if(null == userId) {
            return new DataOutput(CommonStatus.UNAUTHORIZED);
        }
        if(null == id) {
            logger.info("编辑内容id不能为空");
            return new DataOutput(CommonStatus.BAD_REQUEST.getCode(), "编辑内容id不能为空");
        }
        if(baseUtils.isBlank(matchingKeyword)) {
            logger.info("匹配字或物流节点状态不能为空");
            return new DataOutput(CommonStatus.BAD_REQUEST.getCode(), "匹配字或物流节点状态不能为空");
        }
        String logisticsStatusMsg = OrderStatusEmnu.NEW_EXPRESSAGE_STATUS.getMsgByCode(logisticsStatus);
        if(logisticsStatusMsg == null) {
            logger.info("匹配字或物流节点状态不能为空");
            return new DataOutput(CommonStatus.BAD_REQUEST.getCode(), "匹配字或物流节点状态不能为空");
        }
        if(null == logisticsStatus) {
            logger.info("物流节点状态码不能为空");
            return new DataOutput(CommonStatus.BAD_REQUEST.getCode(), "物流节点状态码不能为空");
        }
        // 判断id是否正确
        ExpressageMatchingDetailModel expressageMatchingDetail = repository.findOne(PredicateBuilder.conjunction().and(() -> Q.id.eq(id)).and(() -> Q.isDelete.eq(false)).get());
        if (expressageMatchingDetail == null) {
            logger.info("未找到原匹配字相关信息");
            return new DataOutput(CommonStatus.BAD_REQUEST.getCode(), "未找到原匹配字相关信息");
        }
        int i = repository.updateByWhere(PredicateBuilder.conjunction().and(() -> Q.id.eq(id)).get(), it -> {
            it.set(Q.createTime, new Date());
            it.set(Q.createBy, userId);
            it.set(Q.logisticsStatus, logisticsStatus);
            it.set(Q.logisticsStatusMsg, logisticsStatusMsg);
            it.set(Q.matchingKeyword, matchingKeyword);
        });
        if(i > 0) {
            return new DataOutput();
        }
        return new DataOutput(CommonStatus.NOT_FOUND);
    }

    /**
     *
     * 功能描述: 根据物流公司id获取关键字对象列表
     *
     * @param companyId 物流公司id
     * @return
     * @auther: Kien
     * @date: 2019/7/5 10:03
     */
    public List<ExpressageMatchingDetailModel> findExpressageMatchingByCompanyId(Long companyId) {
        if(null == companyId) {
            return null;
        }
        return repository.findAll(PredicateBuilder.conjunction()
                .and(() -> Q.servicesCompanyId.eq(companyId))
                .and(() -> Q.isDelete.eq(false))
                .get());
    }

    /**
     *
     * 功能描述: 根据物流公司id获取关键字信息列表
     *
     * @param
     * @return
     * @auther: Kien
     * @date: 2019/7/5 10:10
     */
    public List<String> findExpressageMatchingDetaiByCompanyId(Long companyId){
        List<ExpressageMatchingDetailModel> expressageMatchings = findExpressageMatchingByCompanyId(companyId);
        if(CollectionUtils.isEmpty(expressageMatchings)) {
            return null;
        }
        // 获取这个物流商的所有关键字
        List<String> list = new ArrayList<>();
        for(ExpressageMatchingDetailModel expressageMatchingDetailModel: expressageMatchings) {
            list.add(expressageMatchingDetailModel.getMatchingKeyword());
        }
        return list;
    }

    /**
     * 判断字符串中是否包含某个字符或者字符串
     * @param param
     * @param s
     * @return
     */
    public boolean isHave(List<String> param,String s){
        /*此方法有两个参数，第一个是规则的字符串数组，第二个是要查找的字符或字符串*/
        int i = 0;
        while (i < param.size()){
            Pattern pattern = Pattern.compile(".*"+param.get(i)+".*");
            Matcher matcher = pattern.matcher(s);
            boolean rs = matcher.find();
            if(rs){
                return true;
            }
            i++;
        }
        return false;
    }

    /**
     *
     * 功能描述: excel 文件上传
     *
     * @param file 文件流
     * @param request
     *
     * @return
     * @auther: Kien
     * @date: 2019/7/1 17:18
     */
    public DataOutput uploadExcel(MultipartFile file, HttpServletRequest request) {
        Long userId = UserSession.getUserIdBySession(request);
        if(null == userId) {
            return new DataOutput(CommonStatus.UNAUTHORIZED);
        }
        try {
            String filename = System.currentTimeMillis() + file.getOriginalFilename();//获取file图片名称
//            String filePath = "D:\\uploadTest"+System.currentTimeMillis()+ "\\";
            String filePath = EXCEL_UPLOAD_LOCATION;
            uploadFile(file.getBytes(), filePath, filename);
            return new DataOutput(filePath + filename);
        } catch (Exception e) {
            logger.error("上传excel异常" + e.getMessage());
            return new DataOutput(CommonStatus.SERVER_ERROR.getCode(), "上传excel异常");
        }
    }

    /**
     *
     * 功能描述: 
     *
     * @param 
     * @return
     * @auther: Kien
     * @date: 2019/7/5 9:59
     */
    public static void uploadFile(byte[] file, String filePath, String fileName) throws Exception {
        File targetFile = new File(filePath);
        if(!targetFile.exists()){
            targetFile.mkdirs();
        }
        FileOutputStream out = new FileOutputStream(filePath+fileName);
        out.write(file);
        out.flush();
        out.close();
    }
}
